﻿using Corina.Commands;
using Corina.Common;
using Corina.Datamodel.DataObject;
using Corina.Datamodel.Entities;
using Corina.Enumerations;
using Corina.Infrastructure;
using Corina.Win.Helpers;
using Corina.Win.ServerHub.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Xml.Linq;

namespace Corina.Win.ServerHub.Viewmodels
{
    public class ServerViewmodel : ViewmodelBase
    {
        Timer timer;
        DispatcherTimer watcherTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
        public ServerViewmodel()
        {
            TimerCallback timerCallback = new TimerCallback(CalculateTime);
            timer = new Timer(timerCallback, "", 0, 1000);
            watcherTimer.Tick += WatcherTimerTick;
            NetworkChange.NetworkAvailabilityChanged -= NetworkAvailabilityChanged;
            NetworkChange.NetworkAvailabilityChanged += NetworkAvailabilityChanged;
        }

        private void WatcherTimerTick(object sender, EventArgs e)
        {
            for (var i = 0; i < DataContext.ClientsCollection.Count; i++)
            {
                var c = DataContext.ClientsCollection[i];
                try
                {
                    //char ch = Convert.ToChar(1);
                    //c.Client.Send(BitConverter.GetBytes(ch), SocketFlags.None);
                    //c.Client.Send(Encoding.ASCII.GetBytes("OK"), SocketFlags.None);
                    //c.Client.Send(new byte[] { 79, 75 }, SocketFlags.None);
                    //c.Client.Send(BitConverter.GetBytes(ch), SocketFlags.None);
                    c.Client.Send(Encoding.UTF8.GetBytes("test"), SocketFlags.None);
                }
                catch
                {
                    c.Client.Close();
                    DataContext.ClientsCollection.Remove(c);
                    RaisePropertyChanged(() => ClientCount);
                    RaisePropertyChanged(() => Top10Client);
                }
            }
        }

        void NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            RaisePropertyChanged(() => CurrentIPAddress);
        }

        #region Timer
        public int RunSeconds
        {
            get
            {
                return Convert.ToInt32(DatetimeHelper.GetDuration(StartUpTime, DateTime.Now));
            }
        }

        private DateTime _startUpTime = DateTime.Now;
        public DateTime StartUpTime
        {
            get
            {
                return _startUpTime;
            }
            set
            {
                _startUpTime = value;
                RaisePropertyChanged(() => StartUpTime);
            }
        }
        #endregion

        #region Server
        public IEnumerable<string> CurrentIPAddress
        {
            get
            {
                string hostName = Dns.GetHostName();
                IPHostEntry entry = Dns.GetHostEntry(hostName);
                return entry.AddressList.Select(t => t.ToString());
            }
        }

        private int _port = 8000;
        public int Port
        {
            get { return _port; }
            set
            {
                _port = value;
                RaisePropertyChanged(() => Port);
            }
        }

        private TcpListener listener;
        private ManualResetEvent tcpClientConnected = new ManualResetEvent(false);
        private Thread mainthread;

        private bool _linstenStarted = false;
        public bool ListenStarted
        {
            get { return _linstenStarted; }
            set
            {
                _linstenStarted = value;
                RaisePropertyChanged(() => ListenStarted);
                DataContext.IsServerRunning = ListenStarted;
                RaisePropertyChanged(() => StartCommand);
                RaisePropertyChanged(() => StopCommand);
            }
        }
        #endregion

        #region Client
        public int ClientCount
        {
            get
            {
                return DataContext.ClientsCollection.Count;
            }
        }

        public IEnumerable<ClientInfo> Top10Client
        {
            get
            {
                return DataContext.ClientsCollection.OrderByDescending(t => t.EntryTime).Take(10);
            }
        }
        #endregion

        #region Methods
        private void CalculateTime(object state)
        {
            RaisePropertyChanged(() => RunSeconds);
        }

        #region Server
        private void StartListening()
        {
            DataContext.ClientsCollection.Clear();
            try
            {
                WriteRunlog("trying to start server.......");
                listener = new TcpListener(IPAddress.Any, Port);
                listener.Start();
                mainthread = new Thread(new ThreadStart(Recieved));
                mainthread.IsBackground = true;
                mainthread.Start();
                ListenStarted = true;
                watcherTimer.Start();
                WriteRunlog("completed!");
            }
            catch (Exception ex)
            {
                WriteRunlog("fault to start server", ex.Message);
            }
        }

        private void StopListening()
        {
            
            listener.Stop();

            listener = null;
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                DataContext.ClientsCollection.Clear();
                RaisePropertyChanged(() => ClientCount);
                RaisePropertyChanged(() => Top10Client);
            }));

            ListenStarted = false;
            mainthread.Abort();
            mainthread = null;
            GC.Collect();
            watcherTimer.Stop();
        }

        private void Recieved()
        {
            while (true)
            {
                tcpClientConnected.Reset();
                listener.BeginAcceptSocket(new AsyncCallback(AsyncCallback), listener);
                tcpClientConnected.WaitOne();
            }
        }

        private void AsyncCallback(IAsyncResult result)
        {
            TcpListener listener = (TcpListener)result.AsyncState;
            try
            {
                Socket handler = listener.EndAcceptSocket(result);
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                {
                    string address = string.Format("{0}:{1}", ((IPEndPoint)handler.RemoteEndPoint).Address, ((IPEndPoint)handler.RemoteEndPoint).Port);
                    if (DataContext.ClientsCollection.Count(t => t.Address == address) == 0)
                    {
                        DataContext.ClientsCollection.Add(new ClientInfo(handler));
                        RaisePropertyChanged(() => ClientCount);
                        RaisePropertyChanged(() => Top10Client);
                        
                        //WriteRunlog(string.Format("{0} --> 连接成功。", address));
                        //OnlineNotify(address);
                        WriteRunlog(address, "connected!");

                        Thread t = new Thread(new ParameterizedThreadStart(Func));
                        t.Start(handler);
                        tcpClientConnected.Set();
                    }
                    else
                    {
                        WriteRunlog(address + " is already exists.");
                    }
                }));

            }
            catch (Exception ex){
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                {
                    WriteRunlog("error occurred", ex.Message);
                }));
            }
        }

        private void Func(object socket)
        {
            while (true)
            {
                Socket client = socket as Socket;
                try
                {
                    byte[] messageBuffer = new byte[1024 * 64];

                    int length = client.Receive(messageBuffer, SocketFlags.None);
                    if (length > 0)
                    {
                        #region Encoding
                        sbyte[] sbs = new sbyte[1024 * 64];
                        Buffer.BlockCopy(messageBuffer, 0, sbs, 0, messageBuffer.Length);
                        Encoding encoding = Encoding.GetEncoding(new IdentifyEncoding().GetEncodingName(sbs));

                        string message = encoding.GetString(messageBuffer, 0, length);
                        #endregion

                        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                        {
                            //WriteRunlog(encoding.EncodingName);
                            //WriteRunlog("data length", length);
                            WriteRunlog(message);
                        }));

                        string rgbCode = RegexHelper.MatchRGBCode(message);
                        if (!string.IsNullOrEmpty(rgbCode))
                        {
                            
                            //List<string> items = RegexHelper.MatchMeasure(message);
                            //if (items.Count > 0)
                            //{
                            //    List<DeviceMessageInfo> device_messages = new List<DeviceMessageInfo>();
                            //    items.ForEach((t) =>
                            //    {
                            //        DeviceMessageInfo device_msg = new DeviceMessageInfo()
                            //        {
                            //            DeviceSn = rgbCode,
                            //            Content = t.Remove(0, 7),
                            //            MobileOrIP = ((IPEndPoint)client.RemoteEndPoint).Address.ToString(),
                            //            TransType = TransTypeValue.TCP,
                            //        };
                            //        device_messages.Add(device_msg);
                            //    });
                            //    if (device_messages.Count > 0)
                            //    {
                            //        InsertMessageHandler handler = new InsertMessageHandler(InsertMessage);

                            //        IAsyncResult result = handler.BeginInvoke(device_messages, new AsyncCallback(InsertMessageCompleted), null);
                            //    }
                            //}
                            int workerThreads, completionPortThreads;

                            ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);

                            ErrorTraceHelper.Trace("\r\n", "available threads", workerThreads);
                            if(workerThreads<5)
                            {
                                Thread.Sleep(1000);
                                continue;
                            }
                            InsertMessageHandler handler = new InsertMessageHandler(InsertMessage);
                            Task task = Task.Factory.StartNew(() =>
                            {
                                try
                                {
                                    string ipaddress = client != null && (client.Available > 0 || client.Connected) ? ((IPEndPoint)client.RemoteEndPoint).Address.ToString() : "";
                                    IAsyncResult result = handler.BeginInvoke(rgbCode, message.Replace("", ""), ipaddress, new AsyncCallback(InsertMessageCompleted), null);
                                }
                                catch (Exception ex)
                                {
                                    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                                    {
                                        WriteRunlog("error occurred ", client, ex.Message);

                                    }));
                                    Thread.CurrentThread.Abort();
                                }
                            });

                        }
                        else if (message.StartsWith("<?xml"))
                        {
                            try
                            {
                                XDocument document = XDocument.Parse(message);
                                XElement root = document.Element("msg");
                                string[] to = root.Element("to").Elements("Target").Select(t => t.Value).ToArray();
                                object[] cmdparams = root.Element("CommandParams").Elements("Param").Select(t => t.Value).ToArray();

                                TransInfo transInfo = new TransInfo()
                                {
                                    MessageType = (MessageTypeValue)Convert.ToInt32(root.Element("MessageType").Value),
                                    MessageId = root.Element("MessageId").Value,
                                    Source = root.Element("Source").Value,
                                    Report = Convert.ToBoolean(root.Element("Report").Value),
                                    Target = to.ToList(),
                                    Paras = cmdparams.ToList()
                                };

                                ProcessCommandHandler processHandler = new ProcessCommandHandler(ProcessCommand);
                                processHandler.BeginInvoke(client, transInfo, new AsyncCallback(ProcessCommandCallback), null);
                            }
                            catch (Exception ex)
                            {
                                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                                {
                                    WriteRunlog(ex.Message);
                                }));

                                //string add = string.Format("{0}:{1}", ((IPEndPoint)client.LocalEndPoint).Address, ((IPEndPoint)client.LocalEndPoint).Port);
                                //string reply = new TransInfo(add, ex.Message, MessageType.Notification, System.Guid.NewGuid().ToString(), BoardType.Single, MessageTargetType.Server, MessageTargetType.Client, new string[] { }).XmlString();
                                //client.Send(Encoding.UTF8.GetBytes(reply), SocketFlags.None);
                            }
                        }
                        else {
                            //Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                            //{
                            //    WriteRunlog(message);
                            //}));
                        }
                    }
                }
                catch (Exception ex)
                {
                    client.Close();
                    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                    {
                        WriteRunlog("error occurred ", client, ex.Message);
                        
                        ////DataContext.ClientsCollection.Remove(DataContext.ClientsCollection.FirstOrDefault(t => t.Client == client));
                        //string address = string.Format("{0}:{1}", ((IPEndPoint)client.RemoteEndPoint).Address, ((IPEndPoint)client.RemoteEndPoint).Port);
                        //ClientInfo ci = DataContext.ClientsCollection.FirstOrDefault(t => t.Address == address);
                        //DataContext.ClientsCollection.Remove(DataContext.ClientsCollection.FirstOrDefault(t => t.Address == address));
                        //RaisePropertyChanged(() => ClientCount);
                        //RaisePropertyChanged(() => Top10Client);
                    }));
                    Thread.CurrentThread.Abort();
                }
                finally
                {
                    GC.Collect();
                }
            }
        }
        #endregion

        #region delegate
        public delegate ResultInfo InsertMessageHandler(string deviceSn, string content, string ipOrMobile);
        public delegate void ProcessCommandHandler(Socket client, TransInfo transInfo);
        #endregion

        #region Device message
        private ResultInfo InsertMessage(string deviceSn, string content, string ipOrMobile)
        {
            ResultInfo result = new ResultInfo(ResultValue.Unknown);
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                {
                    ErrorTraceHelper.Trace("\r\n", deviceSn, content, ipOrMobile);
                    try
                    {
                        Task<ResultInfo> task = DeviceMeasureClient.InsertBatchDeviceMessageAsync(Account, Password, deviceSn, content, ipOrMobile);
                        task.ContinueWith(t =>
                        {
                            if (task.IsCompleted)
                            {
                                if (task.Exception == null)
                                {
                                    result = task.Result;
                                }
                                else
                                {
                                    ErrorTraceHelper.Trace(task.Exception);
                                    result = new ResultInfo(ResultValue.Failed);
                                }
                            }
                            else
                            {
                                result = new ResultInfo(ResultValue.TimeOut);
                            }
                            task.Dispose();
                        });
                    }
                    catch(Exception ex)
                    {

                    }
                }));
            return result;
        }

        private void InsertMessageCompleted(IAsyncResult result)
        {
            InsertMessageHandler handler = (InsertMessageHandler)((AsyncResult)result).AsyncDelegate;
            ResultInfo resultInfo = handler.EndInvoke(result);
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                WriteRunlog(resultInfo.Result);
            }));
        }
        #endregion

        #region Process data
        private void ProcessCommand(Socket client, TransInfo transInfo)
        {
            switch (transInfo.MessageType)
            {
                case MessageTypeValue.Online:
                    SendOnlineList(client);
                    break;
                case MessageTypeValue.Query:
                    ProcessQueryCommand(client, transInfo);
                    break;
            }
        }

        private void ProcessCommandCallback(IAsyncResult result)
        {
            ProcessCommandHandler handler = (ProcessCommandHandler)((AsyncResult)result).AsyncDelegate;

        }

        private void SendOnlineList(Socket client)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                IEnumerable<string> clients = DataContext.ClientsCollection.Select(t => t.Address);
                TransInfo trans = new TransInfo(MessageTypeValue.Online, "", clients.ToArray());
                client.Send(trans.GetBuffer(), SocketFlags.None);
            }));

        }

        private void ProcessQueryCommand(Socket client, TransInfo transinfo)
        {
            List<object> paras = transinfo.Paras;
        }
        #endregion

        #endregion

        #region Commands
        public ICommand StartCommand
        {
            get
            {
                return new GenericCommand()
                {
                    CanExecuteCallback = delegate { return !ListenStarted; },
                    ExecuteCallback = delegate
                    {
                        StartListening();
                    }
                };
            }
        }

        public ICommand StopCommand
        {
            get
            {
                return new GenericCommand()
                {
                    CanExecuteCallback = delegate { return ListenStarted; },
                    ExecuteCallback = delegate { StopListening(); }
                };
            }
        }
        #endregion
    }
}
